final

Author

최규빈

Published

December 18, 2022

기말고사

주의: 엑셀 등을 이용하여 자료를 전처리할 경우 부분점수 없이 0점 처리함

import pandas as pd 
import numpy as np
import json 
import requests 
import folium 
import plotly.express as px
from plotnine import *

1. 시군구별 에너지사용량 시각화 I (30점)

아래의 주소들에서 2018-2021의 시군구별 에너지 사용량에 대한 자료를 정리하고 물음에 답하라.

# 2018
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2018.csv
...
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Jeju-do2018.csv

# 2019
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2019.csv
...
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Jeju-do2019.csv

# 2020
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2020.csv
...
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Jeju-do2020.csv

# 2021
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2021.csv
...
https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Jeju-do2021.csv

hint1: 코드1, 코드2를 적절하게 응용하면 쉽게 데이터를 합칠 수 있음

## 코드1 
_district = [global_dict['features'][i]['properties']['name_eng'] for i in range(17)]
_year = ['2018','2019','2020','2021']
for d in _district:
    for y in _year: 
        print(d+y)
        
## 코드2
_url = 'https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/{}.csv' 
pd.concat([pd.read_csv(_url.format(k)) for k in ['Seoul2018','Jeju-do2018']])

hint2: 데이터프레임을 읽었을 때 ['에너지사용량(TOE)/지역난방']의 자료형이 통일되어 있지 않음을 유의하여 처리할 것.

pd.read_csv('https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Gangwon-do2021.csv')['에너지사용량(TOE)/지역난방'].dtype
dtype('int64')
pd.read_csv('https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/Seoul2021.csv')['에너지사용량(TOE)/지역난방'].dtype
dtype('O')

hint3: 아래의 코드를 이용하여 geojson 파일을 확보하고 문제를 풀 것

global_dict = json.loads(requests.get('https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-provinces-2018-geo.json').text)
local_dict = json.loads(requests.get('https://raw.githubusercontent.com/southkorea/southkorea-maps/master/kostat/2018/json/skorea-municipalities-2018-geo.json').text)

hint4: 필요하다면 아래의 코드를 활용할 것 (활용하지 않아도 무방함)

_df = pd.DataFrame({'A':['서초구','강남구','송파구'],'B':['33,231','22,321',45123]})
_df                   
A B
0 서초구 33,231
1 강남구 22,321
2 송파구 45123

(할당)

_df.assign(C=2018,D='Seoul')
A B C D
0 서초구 33,231 2018 Seoul
1 강남구 22,321 2018 Seoul
2 송파구 45123 2018 Seoul

(인덱스)

_df.assign(C=2018,D='Seoul').set_index(['A','D'])
B C
A D
서초구 Seoul 33,231 2018
강남구 Seoul 22,321 2018
송파구 Seoul 45123 2018

(applymap)

_df.assign(C=2018,D='Seoul').set_index(['A','D']).\
applymap(lambda x: int(str(x).replace(',','')))
B C
A D
서초구 Seoul 33231 2018
강남구 Seoul 22321 2018
송파구 Seoul 45123 2018

(1) 아래의 지역에 대한 4년간 전기 에너지 사용량의 총합을 구하고 folium을 이용하여 시각화 하라.

[global_dict['features'][i]['properties']['name_eng'] for i in range(17)]
['Seoul',
 'Busan',
 'Daegu',
 'Incheon',
 'Gwangju',
 'Daejeon',
 'Ulsan',
 'Sejongsi',
 'Gyeonggi-do',
 'Gangwon-do',
 'Chungcheongbuk-do',
 'Chungcheongnam-do',
 'Jeollabuk-do',
 'Jeollanam-do',
 'Gyeongsangbuk-do',
 'Gyeongsangnam-do',
 'Jeju-do']
  • location = [36,128], zoom_start=7 로 설정

(풀이)

- 단계1: 데이터프레임 정리

_district_eng = [global_dict['features'][i]['properties']['name_eng'] for i in range(17)]
_year = ['2018','2019','2020','2021']
_url = 'https://raw.githubusercontent.com/guebin/DV2022/main/posts/Energy/{}.csv'
df= pd.concat([pd.read_csv(_url.format(d+y)).assign(year=y,district=d) for d in _district_eng for y in _year])\
.reset_index(drop=True)\
.set_axis(['지역local','건물동수','연면적','전기','도시가스','지역난방','연도','지역global'],axis=1)\
.set_index(['지역global','지역local'])\
.applymap(lambda x: int(str(x).replace(',',''))).reset_index()
df
지역global 지역local 건물동수 연면적 전기 도시가스 지역난방 연도
0 Seoul 종로구 17929 9141777 64818 82015 111 2018
1 Seoul 중구 10598 10056233 81672 75260 563 2018
2 Seoul 용산구 17201 10639652 52659 85220 12043 2018
3 Seoul 성동구 14180 11631770 60559 107416 0 2018
4 Seoul 광진구 21520 12054796 70609 130308 0 2018
... ... ... ... ... ... ... ... ...
995 Jeju-do 서귀포시 34729 7233931 34641 1306 0 2019
996 Jeju-do 제주시 66504 19819923 99212 22179 0 2020
997 Jeju-do 서귀포시 34880 7330040 35510 1639 0 2020
998 Jeju-do 제주시 67053 20275738 103217 25689 0 2021
999 Jeju-do 서귀포시 35230 7512206 37884 2641 0 2021

1000 rows × 8 columns

- 단계2: 시각화할 자료 정리

df.groupby(['지역global'])['전기'].sum().reset_index()
지역global 전기
0 Busan 2860998
1 Chungcheongbuk-do 1413048
2 Chungcheongnam-do 1795949
3 Daegu 1802007
4 Daejeon 1221429
5 Gangwon-do 1491560
6 Gwangju 1154501
7 Gyeonggi-do 9084472
8 Gyeongsangbuk-do 2045804
9 Gyeongsangnam-do 2450436
10 Incheon 1988226
11 Jeju-do 536540
12 Jeollabuk-do 1416208
13 Jeollanam-do 1341756
14 Sejongsi 263956
15 Seoul 9331349
16 Ulsan 779840

- 단계3: 시각화

m = folium.Map(
    location = [36,128],
    zoom_start=7,
    scrollWheelZoom = False
)
folium.Choropleth(
    geo_data=global_dict,
    data=df.groupby(['지역global'])['전기'].sum().reset_index(),
    columns=['지역global','전기'],
    key_on='properties.name_eng',
).add_to(m)
m
Make this Notebook Trusted to load map: File -> Trust Notebook

(2) 서울의 4년간 전기에너지 사용량의 총합을 구하고 folium을 이용하여 구별로 시각화 하라.

hint1: 아래의 리스트에서

[local_dict['features'][i]['properties']['code'] for i in range(250)]

11로 시작하는 원소들이 서울지역이다.

hint2: 서울특별시의 “중구”는 유일한 구 이름이 아님을 유의하여 자료를 처리할 것. (예를들어 부산에도 “중구”, 대구에도 “중구”가 존재함)

(풀이)

- 단계1: local_dict2 정리

local_dict2 = local_dict.copy() 
_features = [local_dict['features'][i] for i in range(250) if local_dict['features'][i]['properties']['code'][:2] == '11']
local_dict2['features'] = _features

- 단계2: 시각화할 자료정리

df.query('지역global=="Seoul"')\
.groupby('지역local')['전기'].sum().reset_index()
지역local 전기
0 강남구 905469
1 강동구 322585
2 강북구 215655
3 강서구 438579
4 관악구 393429
5 광진구 307727
6 구로구 326921
7 금천구 218396
8 노원구 354521
9 도봉구 205777
10 동대문구 300022
11 동작구 298972
12 마포구 444166
13 서대문구 286668
14 서초구 650482
15 성동구 300004
16 성북구 344506
17 송파구 654551
18 양천구 339078
19 영등포구 459870
20 용산구 251535
21 은평구 339143
22 종로구 313612
23 중구 394690
24 중랑구 264991

- 단계3: 시각화

m = folium.Map(
    location = [37.55,127],
    zoom_start=11,
    scrollWheelZoom = False
)
folium.Choropleth(
    geo_data=local_dict2,
    data=df.query('지역global=="Seoul"').groupby('지역local')['전기'].sum().reset_index(),
    columns=['지역local','전기'],
    key_on='properties.name',
).add_to(m)
m
Make this Notebook Trusted to load map: File -> Trust Notebook
  • location = [37.55,127], zoom_start=11 로 설정

(3) 서울의 전기에너지 사용비율을 (연도별,구별)로 구하고 이를 plotly의 choropleth_mapbox를 이용하여 시각화 하라. (연도에 따라 choropleth map이 바뀌도록 시각화 할 것)

hint1: 2020년의 관악구의 전기에너지 사용비율은 아래와 같이 계산한다.

\(\frac{\text{2020관악구의 ``에너지사용량(TOE)/전기''}}{\text{2020관악구의 ``에너지사용량(TOE)/전기''}+\text{2020관악구의 ``에너지사용량(TOE)/도시가스''}+\text{2020년 관악구의 ``에너지사용량(TOE)/지역난방''}}\)

hint2: 아래의 코드 참고할 것.

fig = px.choropleth_mapbox(data_frame=???,
                           geojson=???, 
                           color=???,
                           locations=???, 
                           featureidkey=???, 
                           center={"lat": 37.55, "lon": 126.95}, 
                           mapbox_style="carto-positron", 
                           animation_frame=???, # 2028,2019,2020,2021와 같이 년도가 명시된 column의 이름을 쓸 것
                           range_color=[0.31,0.56],
                           height=800,
                           zoom=10)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

(풀이)

- 1단계: 시각화할 자료정리

df.eval('전기사용비율 = 전기/(전기+도시가스+지역난방)')\
.query('지역global=="Seoul"')
지역global 지역local 건물동수 연면적 전기 도시가스 지역난방 연도 전기사용비율
0 Seoul 종로구 17929 9141777 64818 82015 111 2018 0.441107
1 Seoul 중구 10598 10056233 81672 75260 563 2018 0.518569
2 Seoul 용산구 17201 10639652 52659 85220 12043 2018 0.351243
3 Seoul 성동구 14180 11631770 60559 107416 0 2018 0.360524
4 Seoul 광진구 21520 12054796 70609 130308 0 2018 0.351434
... ... ... ... ... ... ... ... ... ...
95 Seoul 관악구 31299 22230193 136043 196643 78 2021 0.408827
96 Seoul 서초구 16686 32824535 242700 181974 39854 2021 0.522466
97 Seoul 강남구 22464 48871036 364645 236446 95742 2021 0.523289
98 Seoul 송파구 22727 38819901 236444 185492 94175 2021 0.458126
99 Seoul 강동구 18715 24990185 110313 144513 16676 2021 0.406306

100 rows × 9 columns

- 2단계: 시각화

fig = px.choropleth_mapbox(data_frame=df.eval('전기사용비율 = 전기/(전기+도시가스+지역난방)').query('지역global=="Seoul"'),
                           geojson=local_dict2, 
                           color='전기사용비율',
                           locations="지역local", 
                           featureidkey="properties.name", 
                           center={"lat": 37.55, "lon": 127}, 
                           mapbox_style="carto-positron", 
                           animation_frame='연도',
                           range_color=[0.31,0.56],
                           height=800,
                           zoom=10)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})

2. 시군구별 에너지사용량 시각화 II (50점)

문제1과 동일한 자료에 대하여 아래의 물음에 답하라.

(1) 전주시 덕진구와 완산구에서 사용하는 전기양은 전라북도 전체의 몇 프로인지 연도별로 계산하고 아래의 예시와 같이 시각화 하라.

(풀이)

- 단계1: 시각화할 자료 정리

data = df.query('지역global=="Jeollabuk-do"')\
.groupby(['연도'])[['전기']].sum().reset_index()\
.rename({'전기':'전라북도전기'},axis=1)\
.merge(df.query('지역global=="Jeollabuk-do"'))\
.eval("전기사용비율=전기/전라북도전기")\
.query('지역local == "덕진구" or 지역local == "완산구"')\
.assign(전기사용비율 = lambda df: np.round(df['전기사용비율']*100,2))
data 
NameError: name 'df' is not defined

- 단계2: 시각화

data.plot.bar(x='연도',y='전기사용비율',backend='plotly',color='지역local',barmode='group',text='전기사용비율')
NameError: name 'data' is not defined

(2) 면적대비 총 에너지사용량이 많은 상위20개의 지역을 찾고 아래의 예시와 같이 시각화 하라.

(풀이)

- 단계1: 시각화할 자료 정리

data = df.eval('면적대비에너지사용량 = (전기+도시가스+지역난방)/ 연면적')\
.assign(면적대비에너지사용량 = lambda df: list(map(lambda x: np.round(x,4),df['면적대비에너지사용량'])))\
.sort_values(by='면적대비에너지사용량',ascending=False).reset_index(drop=True).iloc[:20]\
.assign(지역연도=lambda df: list(map(lambda x,y: x+'/'+str(y), df['지역local'],df['연도'])))\
.rename({'지역연도':'지역/연도'},axis=1)
data
NameError: name 'df' is not defined
data.plot.bar(x='지역/연도',y='면적대비에너지사용량',backend='plotly')
NameError: name 'data' is not defined

서울특별시와 경기도에 속한 구역을 아래와 같이 구분하여 시각화 하라.

data.plot.bar(x='지역/연도',y='면적대비에너지사용량',backend='plotly',color='지역global')
NameError: name 'data' is not defined

(3) 각 시도별로 4년간 에너지사용량(=전기+도시가스+지역난방)이 가장 많은 2개의 구를 뽑고 아래와 같이 시각화 하라.

(풀이)

- 단계1: 에너지사용량을 계산

df.eval('에너지사용량=전기+도시가스+지역난방')
NameError: name 'df' is not defined

- 단계2: 4년간 에너지사용량을 구함

df.eval('에너지사용량=전기+도시가스+지역난방')\
.groupby(['지역local','지역global'])\
.agg({'에너지사용량':np.sum}).reset_index().rename({'에너지사용량':'4년간에너지사용량'},axis=1)
NameError: name 'df' is not defined

- 단계3: ’지역global’로 그룹핑된 리스트 생성

lst = df.eval('에너지사용량=전기+도시가스+지역난방')\
.groupby(['지역local','지역global'])\
.agg({'에너지사용량':np.sum}).reset_index().rename({'에너지사용량':'4년간에너지사용량'},axis=1)\
.groupby('지역global')\
.pipe(list)
lst[0] # 편의상 리스트의 첫원소만 출력 
NameError: name 'df' is not defined

- 단계4: 단계3의 결과를 이용하여 4년간에너지사용량이 가장 큰 2개의 구만 선택

data = pd.concat(list(map(lambda x: x[1].sort_values(by='4년간에너지사용량',ascending=False).reset_index(drop=True).iloc[:2],lst))).reset_index(drop=True)
data
NameError: name 'pd' is not defined

- 단계5: 시각화

data.plot.bar(backend='plotly',y='4년간에너지사용량',color='지역global',text='지역local')
NameError: name 'data' is not defined

(4) ‘Seoul’, ‘Incheon’, ‘Gyeonggi-do’ 지역을 수도권으로 그 외의 지역은 비수도권으로 구분하라.

(풀이)

- 단계1: 시각화를 위한 데이터정리

data= df.assign(MetropolitanArea=list(map(lambda x: x in ['Seoul', 'Incheon', 'Gyeonggi-do'],df['지역global'])))\
.rename({'전기':'Elec','도시가스':'Gas','연도':'Year'},axis=1)\
.loc[:,['Elec','Gas','Year','MetropolitanArea']]\
.set_index(['Year','MetropolitanArea'])\
.stack()\
.reset_index()\
.rename({'level_2':'EnergyType',0:'Consumption'},axis=1)
data
NameError: name 'df' is not defined

- 단계2: 시각화

ggplot(data)+\
geom_boxplot(aes(x='EnergyType',y='Consumption',color='EnergyType'))+\
facet_grid('MetropolitanArea~Year') 
NameError: name 'ggplot' is not defined

3. 심슨의 역설 (20점)

아래는 1973년 가을학기 버클리대학의 입학통계이다.

pd.read_csv("https://raw.githubusercontent.com/guebin/DV2022/master/posts/Simpson.csv",index_col=0,header=[0,1])
NameError: name 'pd' is not defined

(1) 남녀합격률을 plotly를 사용하여 시각화 하라.

(풀이)

- 단계0: 강의노트와 동일한 방식으로 df, data 생성

df=pd.read_csv("https://raw.githubusercontent.com/guebin/DV2022/master/posts/Simpson.csv",index_col=0,header=[0,1])\
.stack().stack().reset_index()\
.rename({'level_0':'department','level_1':'result','level_2':'gender',0:'count'},axis=1)
df.head()
NameError: name 'pd' is not defined
data= df.groupby(['gender','result']).agg({'count':np.sum}).reset_index()\
.merge(df.groupby('gender').agg({'count':np.sum}).reset_index().rename({'count':'count2'},axis=1))\
.eval('rate = count/count2').query('result=="pass"')
data.head()
NameError: name 'df' is not defined

- 단계1: 소수점처리

data.assign(rate = np.round(data['rate']*100,2))
NameError: name 'data' is not defined

- 단계2: 시각화

data.assign(rate = np.round(data['rate']*100,2))\
.plot.bar(backend='plotly',y='gender',x='rate',color='gender',barmode='group',text='rate')
NameError: name 'data' is not defined

(2) 학과별 남녀합격률을 plotly를 사용하여 시각화 하라.

(풀이)

- 단계0: 강의노트와 동일한 방식으로 data2 생성

data2=df.merge(df.groupby(['department','gender']).agg({'count':np.sum}).reset_index().rename({'count':'count2'},axis=1))\
.eval('rate = count/count2').query('result=="pass"')
data2
NameError: name 'df' is not defined

- 단계1: 시각화

data2.plot.bar(backend='plotly',x='rate',y='gender',color='gender',barmode='group',facet_row='department',height=900)
NameError: name 'data2' is not defined